feat(cli): resolve issue workflow state by name on issues update#31
Merged
Conversation
Add a reusable workflow-state resolver in linear-core that maps a state
reference (UUID or human name) to a stateId for a team, and wire it into
`issues update` so agents can set state with `--state "In Progress"` or
`--input '{"state":"..."}'`/`'{"stateName":"..."}'`. Resolution is scoped
to the target issue's team; an explicit stateId UUID still passes through
unchanged; state/stateName keys are stripped before reaching GraphQL.
Unknown or ambiguous names error with the team's valid states listed.
Refs: #22
There was a problem hiding this comment.
Pull request overview
Adds a reusable workflow-state name/UUID resolver in @wiseiodev/linear-core and wires it into linear issues update so an issue's state can be set by name via either the global --state flag or state/stateName keys inside --input. Resolution is scoped to the target issue's team, raw stateId UUIDs pass through unchanged, and unknown/ambiguous names produce typed errors that enumerate valid states.
Changes:
- New
resolveStateId+isWorkflowStateIdcore module with UUID short-circuit, case-insensitive name match, optionalpreferredTypefallback (lowestpositionfirst, missing positions sort last), and listing-aware error messages. - New
LinearGateway.listWorkflowStatesForTeam(server-sideteam.id.eqfilter, single page of 250) and a newpositionfield onWorkflowStateRecord. - CLI: new
normalizeIssueUpdateStatePayloadfolds--state/state/stateNameintostateId, strips name keys, and skips the issue fetch for UUID refs or explicit non-emptystateId;registerResourceCommandgains an opt-inallowEmptyInputfor update soissues update --state …works without--input.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| packages/linear-core/src/entities/state-resolver.ts | New resolver module: UUID guard, name matching, preferred-type fallback, typed errors. |
| packages/linear-core/src/entities/linear-gateway.ts | Adds listWorkflowStatesForTeam and maps the new position field. |
| packages/linear-core/src/entities/models.ts | Adds optional position to WorkflowStateRecord. |
| packages/linear-core/src/index.ts | Re-exports the new resolver module. |
| packages/linear-core/tests/state-resolver.test.ts | 8 Vitest cases covering passthrough, scoping, fallback, not-found, ambiguity. |
| packages/cli/src/commands/issue-state.ts | New normalizeIssueUpdateStatePayload + IssueStateGateway interface. |
| packages/cli/src/commands/input.ts | New parseOptionalJsonInput that returns {} when no input provided. |
| packages/cli/src/commands/resource.ts | Adds ResourceCommandOptions.update.allowEmptyInput and routes parsing accordingly. |
| packages/cli/src/index.ts | Wires the normalizer into issues update and opts the command into empty input. |
| packages/cli/src/help/resource-help.ts | Adds --state and state-key examples to issues update help. |
| packages/cli/tests/issue-state.test.ts | 10 Vitest cases for flag/JSON key paths, stateId precedence, UUID short-circuit, errors. |
| .reports/issue-22.html, .reports/issue-22-qa.md | Work/QA reports documenting verification. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Surface the new state-by-name capability where agents will see it: a "Set Issue State By Name" section plus a "common mistakes -> correct command" table in the bundled linear-cli skill, a setter-guidance block in `issues update --help` (new optional notes on update help), a README example, and concrete state-by-name hints in the issue-triage and cycle-planning skills. Locked by a skills-catalog test assertion. Refs: #22
8 tasks
|
🎉 This PR is included in version 1.6.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What shipped
resolveStateIdmodule in@wiseiodev/linear-corethat maps a state reference (a UUID or a human name) to astateIdfor a team, with an optional preferred-typefallback reserved for later slices.linear issues update ANN-1 --state "In Progress"now works with no--inputrequired.--input '{"state":"In Progress"}'and--input '{"stateName":"In Progress"}'resolve to the rightstateId.teamId); a rawstateIdUUID still passes through unchanged.name (type);state/stateNamekeys are stripped before the payload reaches GraphQL.listWorkflowStatesForTeamgateway method (server-side team filter) and apositionfield onWorkflowStateRecord.linear-cliskill, a setter-guidance block inissues update --help, a README example, and concrete state-by-name hints in theissue-triageandcycle-planningskills.Dependencies collected
Files changed
Tests added
packages/linear-core/tests/state-resolver.test.ts— 8 Vitest cases (UUID passthrough, case-insensitive name match, team scoping, preferred-type fallback incl. position-less states, not-found, ambiguous).packages/cli/tests/issue-state.test.ts— 10 Vitest cases (--stateflag,state/stateNamekeys, explicit stateId untouched, empty stateId no longer pre-empts, padded UUID short-circuit, precedence, no-state passthrough, unknown name error).packages/skills-catalog/tests/skills-catalog.test.ts— +1 case asserting the bundled skill documents state-by-name.Quality gates
pnpm check:write(biome)pnpm typecheck(turbo, 4 pkgs)pnpm test(linear-core 58, cli 60, skills-catalog 7)pnpm build(turbo, 4 pkgs)Adversarial review
--stateas the setter on update is PRD-mandated ("no new flag required") — the update help now documents the setter usage; (b)issues bulk-updatestate-by-name is owned by sibling issue State-by-name on issues create + bulk-update #27 and is untouched/unregressed here.positionnow sorts last in the type fallback; only a non-emptystateIdpre-empts resolution;asStringtrims so padded UUIDs short-circuit; explanatory comments added for the UUID short-circuit and the 250-state page cap.Self-QA
See QA fallback log for the per-acceptance-criterion verification trail (deterministic unit tests, a runtime demonstration against the built
@wiseiodev/linear-coremodule, a CLI help smoke test, and the docs/hints surfaces verified viaissues update --help). This is a terminal CLI / SDK change with no browser surface, so a Playwright video is not applicable.Acceptance criteria
issues update <id> --state "<name>"sets the issue to the matching state--input '{"stateName":"<name>"}'and--input '{"state":"<name>"}'resolve to astateId--input '{"stateId":"<uuid>"}'continues to work unchangedstate/stateNamekeys never reach GraphQLissues updatenormalization is tested (flag + JSON-key paths, stateId untouched)Commits
Linear
Refs: #22